

import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;

import javax.microedition.io.Connector;
import javax.microedition.io.file.FileConnection;
import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Display;
import javax.microedition.media.Manager;
import javax.microedition.media.MediaException;
import javax.microedition.media.Player;
import javax.microedition.media.PlayerListener;
import javax.microedition.media.control.MetaDataControl;
import javax.microedition.media.control.VolumeControl;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

import player.MP3Metadata;
import state.State;
import tool.EnumerationUtils;
import tool.FileConnectionUtils;
import tool.KeyCode;
import tool.Path;
import tool.PlayerUtils;
import tool.RamPerfo;
import tool.TimePerfo;

import com.nokia.mid.impl.isa.ui.DeviceControl;

import db.DBReader;

import function.ChainMp3;
import function.Mp3File;
import function.Navigator;
import function.now.NowManager;
import function.playlist.NowPlaylist;
import function.playlist.Playlist;
import function.search.DBSearchFunction;
import function.search.SearchFunction;
import function.search.SearchFunctionImpl;
import function.stat.IntegerStore;
import function.stat.Stats;
import graphics.screens.ArtistSearchScreen;
import graphics.screens.DeleteNewScreen;
import graphics.screens.DeleteReportScreen;
import graphics.screens.LockScreen;
import graphics.screens.MainScreen;
import graphics.screens.NewListScreen;
import graphics.screens.PopUpNameScreen;
import graphics.screens.PopUpPlaylistModeScreen;
import graphics.screens.StatsScreen;

public class OldBoot extends MIDlet implements PlayerListener {

	private static String[] DIR_NAMES = new String[] { "file:///E:/_Calme",
			"file:///E:/_Good", "file:///E:/_Energie",
			"file:///E:/_Styles/Hip Hop", "file:///E:/_Styles/Poubelle",
			Path.BOOT };

	private static OldBoot instance;

	private State state = null;

	private Player player;
	private Navigator navigator;
	private MainScreen titleSearchScreen;
	private ArtistSearchScreen artistSearchScreen;
	private NewListScreen newListScreen;
	private PopUpPlaylistModeScreen playslistScreen = null;

	private SearchFunctionImpl wickedSearchFunction;

	private Hashtable alreadyPlayed = new Hashtable();

	// private int dirIndex;

	/*
	 * dir <String> |- playlist <Playlist>
	 */
	private Hashtable playlists;
	/*
	 * dir <String> |- <String[]> |- mp3name <String>
	 */
	private Hashtable mp3sLists;

	public static final int HIP_HOP_CODE = -200;
	public static final int GOOD_MODE = -201;

	private int nightModeIndex;

	private Timer pauseTimer = null;

	private SearchFunction currentSearchFunction = null;

	public OldBoot() throws Throwable {
		RamPerfo.startMeasure();
		TimePerfo.startMeasure();
		
		if (instance == null) {
			instance = this;
		}
		DeviceControl.setLights(0, 100);
		state = State.IDLE;
		state.add(State.MODE_SEARCH_BY_TITLE);
	}

	private void setupNewListScreen() {
		String newDirPath = Path.NOW;
		FileConnection connection;
		int count = 0;
		Enumeration mp3s = null;

		try {
			connection = (FileConnection) Connector.open(newDirPath);
			mp3s = connection.list("*.link", true);
			count = EnumerationUtils.getCount(mp3s);
			mp3s = connection.list("*.link", true);
			connection.close();
		} catch (IOException e) {
			e.printStackTrace();
		}

		Mp3File[] newMp3s = new Mp3File[count];
		int index = 0;

		while (mp3s.hasMoreElements()) {
			String mp3 = (String) mp3s.nextElement();
			Mp3File mp3File = new Mp3File(newDirPath, mp3);
			newMp3s[index++] = mp3File;
		}
		newListScreen = new NewListScreen(newMp3s);
	}

	private void setupMp3sLists() {
		try {
			mp3sLists = new Hashtable();
			Enumeration playlistKeys = playlists.keys();

			while (playlistKeys.hasMoreElements()) {
				String dir = (String) playlistKeys.nextElement();
				String[] mp3s = null;
				if (Path.BOOT.equals(dir)) {
					mp3s = FileConnectionUtils.getNowMp3sList();
				} else {
					mp3s = FileConnectionUtils.getMp3sList(dir);
				}
				mp3sLists.put(dir, mp3s);
			}
		} catch (Throwable e) {
			alert(e);
		}
	}

	private void setupPlaylists() {
		String[] messages = new String[] { "Calme", "Good", "Energie",
				"Hip Hop", "Poubelle", "New" };

		playlists = new Hashtable();
		for (int i = 0; i < messages.length; i++) {
			String dirName = DIR_NAMES[i];
			if (Path.BOOT.equals(dirName)) {
				playlists.put(Path.BOOT, new NowPlaylist(messages[i], dirName));
			} else {
				playlists.put(dirName, new Playlist(messages[i], dirName));
			}
		}
	}

	protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
		try {
			if (player != null) {
				player.stop();
			}
			player = null;
		} catch (Throwable t) {
			alert(t);
		}
	}

	protected void pauseApp() {

	}

	protected void startApp() throws MIDletStateChangeException {
		addMeasure("After startApp()");
		try {
			setupDisplay();
			wrappedStart();

		} catch (Throwable t) {
			alert(t);
		}
	}

	private void setupDisplay() throws Throwable {
		titleSearchScreen = new MainScreen();
		artistSearchScreen = new ArtistSearchScreen();
		playslistScreen = new PopUpPlaylistModeScreen();
		setDisplay(playslistScreen);
	}

	public void readyToSetup() {
		setDisplay(titleSearchScreen);
	}

	private void wrappedStart() throws Throwable {

		IntegerStore.setValue(IntegerStore.AVERAGE_DURATION, 239);
		
//		Perfo.start();
		
		setupNewListScreen();
		addMeasure("After setupNewList()");
		setupPlaylists();
		addMeasure("After setupPlaylist()");
		setupMp3sLists();
		addMeasure("After setupMp3sLists()");
		addState(State.PLAYLISTS_READY);
		alert("Playlists ready", 1000);
//		Perfo.stop("Playlists ready in ");
//		Perfo.start();
		setupSearch();
		addMeasure("After setupSearch()");
//		Perfo.stop("Searches ready in ");

		titleSearchScreen.setSearchAvailable(true);
		addState(State.SEARCH_READY);
		titleSearchScreen.repaint();
		int collectionSize = wickedSearchFunction.getMp3Number();
		IntegerStore.setValue(IntegerStore.COLLECTION_SIZE, collectionSize);
		// alert(mp3number + " tunes");
		alert("Stats available", 2000);
		Stats.setupSizes();
	}

	public static void addMeasure(String title) {
		RamPerfo.addMeasure(title);
		TimePerfo.addMeasure(title);
	}

	private void setupSearch() {
		// TODO TEMP Just for build
//		wickedSearchFunction = new SearchFunctionImpl(this, new String[] {});
		try {
			wickedSearchFunction.setup();
		} catch (Throwable e) {
			e.printStackTrace();
		}
		currentSearchFunction = wickedSearchFunction;
	}

//	private void setupSearch() {
//		wickedSearchFunction = new DBSearchFunction(new String[] {});
//		currentSearchFunction = wickedSearchFunction;
//		DBReader dbReader = new DBReader();
//		dbReader.addListener(wickedSearchFunction);
//		dbReader.readDB();
//	}
	
	private Mp3File getRandomMP3() {
		String dirName = null;
		int index = -1;

		if (state.equals(State.ANY_MODE)) {
			if (state.equals(State.WICKED_KEY_PRESSED)) {
				index = new Random().nextInt(DIR_NAMES.length - 2);
			} else {
				index = new Random().nextInt(DIR_NAMES.length - 3);
			}
			dirName = DIR_NAMES[index];
		} else if ((navigator != null) && (navigator.getCurrent() != null)) {
			dirName = navigator.getCurrent().getDir();
		} else {
			dirName = "file:///E:/_Good";
		}

		return getRandomMP3(dirName);
	}

	private Mp3File getRandomMP3(String dirName) {
		String[] mp3s = (String[]) mp3sLists.get(dirName);
		Playlist playlist = (Playlist) playlists.get(dirName);

		String mp3name = null;

		while (mp3name == null) {
			int index = new Random().nextInt(playlist.getSize());
			mp3name = mp3s[index];
			if (alreadyPlayed.containsKey(mp3name)) {
				mp3name = null;
			} else {
				alreadyPlayed.put(mp3name, mp3name);
			}
		}
		return new Mp3File(dirName, mp3name);
	}

	private void sandboxRunMp3(Mp3File mp3) {
		try {
			runMP3(mp3);
		} catch (Throwable e) {
			alert(e);
		}
	}

	private void runMP3(Mp3File mp3) throws Throwable {

		if (mp3 == null) {
			return;
		}

		String rawTitle = mp3.getFilename();
		String currentMp3Title = rawTitle.substring(0, rawTitle.length() - 4);
		titleSearchScreen.repaint();

		if (player != null) {
			destroyPlayer();
		}
		player = Manager.createPlayer(mp3.getDir() + '/' + rawTitle);
		player.realize();

		destroyPauseTimer();

		if (state.equals(State.NIGHT_MODE)) {
			if (nightModeIndex < 1) {
				alert("Time to sleep");
				state.remove(State.NIGHT_MODE);
				return;
			} else {
				alert(nightModeIndex + " tunes before sleep");
				VolumeControl volumeControl = PlayerUtils
						.getVolumeControl(player);
				if (volumeControl != null) {
					volumeControl.setLevel(PlayerUtils
							.getVolumeValue(nightModeIndex));
				}
			}
			nightModeIndex--;
		} else {
			setMaxVolume();
		}

		if (navigator == null) {
			navigator = new Navigator(mp3);
		}

		player.prefetch();

		if (!state.equals(State.PAUSE_MP3)) {
			player.start();
			state.add(State.PLAY_MP3);
		}
		titleSearchScreen.handleCommands();

		player.addPlayerListener(this);
		MetaDataControl metadatas = (MetaDataControl) player
				.getControl("javax.microedition.media.control.MetaDataControl");
		// title, author, album, date, genre
		MP3Metadata metadata = new MP3Metadata(currentMp3Title, metadatas
				.getKeyValue("author"));
		titleSearchScreen.setMetadata(metadata);
	}

	private void destroyPauseTimer() {
		if (pauseTimer != null) {
			pauseTimer.cancel();
			pauseTimer = null;
		}
	}

	public static void alert(Throwable t) {
		Alert alert = new Alert("Error", t.getMessage(), null, AlertType.ERROR);
		Display.getDisplay(instance).setCurrent(alert);
	}

	public static void debugAlert(String message) {
		Alert alert = new Alert("Debug", message, null, AlertType.INFO);
		Display.getDisplay(instance).setCurrent(alert);
	}

	public void playNextMP3() throws Throwable {
		destroyPlayer();
		playPause();
	}

	private void destroyPlayer() throws MediaException {
		if (player != null) {
			player.stop();
			player.close();
			player = null;
		}
		titleSearchScreen.handleCommands();
	}

	private void playPause() throws Throwable {

		alert("Pause", 3000);
		titleSearchScreen.setTitle("");

		state.remove(State.PAUSE_MP3);
		state.remove(State.PLAY_MP3);

		destroyPauseTimer();

		pauseTimer = new Timer();
		pauseTimer.schedule(new TimerTask() {

			public void run() {
				pauseTimer = null;
				secureGotoNext();
				securePlayCurrentMp3();
			}

		}, 35 * 1000);

	}

	public void playerUpdate(Player aPlayer, String event, Object data) {
		if (event.equals(PlayerListener.END_OF_MEDIA)) {
			try {
				titleSearchScreen.handleCommands();
				titleSearchScreen.setTitle(null);

				if (state.equals(State.PLAYLIST_MODE)) {
					playNextMP3();
				}
			} catch (Throwable t) {
				alert(t);
			}
		}
	}

	public void alert(String message) {
		alert(message, 3000);
	}

	public void alert(String message, int timeout) {
		titleSearchScreen.alert(message, timeout);
	}

	public void keyPressed(int keyCode) {
		
		if (keyCode == KeyCode.NINE) {
			RamPerfo.displayCurrent();
		}
		
		if (!state.equals(State.PLAYLISTS_READY)) {
			alert("Not ready yet", 1000);
			return;
		}

		if (keyCode == KeyCode.ASTERISK) {
			state.add(State.MODE_CHOICE);
		}

		if (keyCode == KeyCode.DIESE) {
			wickedKeyPressed();
		}

		if (keyCode == KeyCode.GAUCHE) {
			secureGotoPrevious();
			securePlayCurrentMp3();
		} else if (keyCode == KeyCode.DROITE) {
			secureGotoNext();
			securePlayCurrentMp3();
		}

		state.remove(State.ANY_MODE);
		Mp3File mp3 = null;

		if (keyCode == KeyCode.ABC) {
			mp3 = getRandomMP3("file:///E:/_Calme");
		} else if (keyCode == KeyCode.DEF) {
			mp3 = getRandomMP3("file:///E:/_Energie");
		} else if (keyCode == KeyCode.GHI) {
			mp3 = getRandomMP3("file:///E:/_Good");
		} else if (keyCode == KeyCode.JKL) {
			state.add(State.ANY_MODE);
			mp3 = getRandomMP3();
		} else if (keyCode == KeyCode.MNO) {
			mp3 = getRandomMP3(Path.BOOT);
		} else if (keyCode == KeyCode.PQRS) {
			if (state.equals(State.WICKED_KEY_PRESSED)) {
				mp3 = getRandomMP3("file:///E:/_Styles/Poubelle");
			}
		} else if (keyCode == KeyCode.TUV) {
			mp3 = getRandomMP3("file:///E:/_Styles/Hip Hop");
		} else if (keyCode == KeyCode.ZERO) {
			if ((navigator != null)
					&& (Path.BOOT.equals(navigator.getCurrent().getDir()))) {
				setDisplay(new DeleteNewScreen(navigator.getCurrent()));
			} else {
				alert("Cannot delete", 1000);
				TimePerfo.displayCurrent();
			}
		}

		if (mp3 != null) {
			secureSetCurrent(mp3);
			showCurrentMode();
			securePlayCurrentMp3();
		}

		// TODO Temporary inhibition of search by artist
		// if ((keyCode == KeyCode.HAUT) || (keyCode == KeyCode.BAS)) {
		// switchSearchModeAndScreen();
		// }
	}

	private void switchSearchModeAndScreen() {
		if (state.equals(State.MODE_SEARCH_BY_TITLE)) {
			state.remove(State.MODE_SEARCH_BY_TITLE);
			state.add(State.MODE_SEARCH_BY_ARTIST);
			setDisplay(artistSearchScreen);
		} else if (state.equals(State.MODE_SEARCH_BY_ARTIST)) {
			state.remove(State.MODE_SEARCH_BY_ARTIST);
			state.add(State.MODE_SEARCH_BY_TITLE);
			setDisplay(titleSearchScreen);
		}
	}

	private void wickedKeyPressed() {
		if (state.equals(State.WICKED_KEY_PRESSED)) {
			state.remove(State.WICKED_KEY_PRESSED);
		} else {
			state.add(State.WICKED_KEY_PRESSED);
		}
	}

	private void secureGotoNext() {
		Mp3File randomMp3 = null;
		try {
			randomMp3 = getRandomMP3();
		} catch (Throwable e) {
			alert(e);
		}

		if (navigator == null) {
			navigator = new Navigator(randomMp3);
			alert("Random tune selected", 1000);
		} else if (!navigator.hasNext()) {
			navigator.setCurrent(randomMp3);
			alert("Random tune selected", 1000);
		} else {
			navigator.gotoNext();
		}
	}

	private void securePlayCurrentMp3() {
		if ((navigator != null) && (navigator.getCurrent() != null)) {
			sandboxRunMp3(navigator.getCurrent());
		}
	}

	private void secureGotoPrevious() {
		if ((navigator != null) && (navigator.getPrevious() != null)) {
			navigator.gotoPrevious();
		}
	}

	private void showCurrentMode() {

		if (state.equals(State.ANY_MODE)) {
			alert("Mode Any", 1000);
		} else if ((navigator != null) && (navigator.getCurrent() != null)) {
			String dir = navigator.getCurrent().getDir();
			String mode = ((Playlist) playlists.get(dir)).getType();
			alert("Mode " + mode, 1000);
		}
	}

	public String[] getDirNames() {
		// return DIR_NAMES;
		return new String[] { "file:///E:/_Calme", "file:///E:/_Good",
				"file:///E:/_Energie", "file:///E:/_Styles/Hip Hop",
				"file:///E:/_Styles/Poubelle", };
	}

	public static OldBoot getInstance() {
		return instance;
	}

	public void searchInputReady(String input) {
		titleSearchScreen.createSearchInput(false);
		titleSearchScreen.repaint();

		ChainMp3 result = null;
		try {
			result = currentSearchFunction.find(input);
		} catch (Throwable t) {
			alert(t);
		}

		if (result != null) {
			try {
				if (result.getNext() != null) {
					state.add(State.SEARCH_CHOICE);
					titleSearchScreen.setSearchChoices(result);
					titleSearchScreen.repaint();
				} else {
					Mp3File onlyEntry = result.getMp3File();
					secureSetCurrent(onlyEntry);
					securePlayCurrentMp3();

					rebootSearch();
				}

			} catch (Throwable t) {
				alert(t);
			}
		} else {
			alert("\"" + input.toUpperCase() + "\" not found");
			rebootSearch();
		}

	}

	private void rebootSearch() {
		state.remove(State.SEARCH_CHOICE);
		state.remove(State.SEARCH_ENGAGED);
		titleSearchScreen.rebootSearch();
		titleSearchScreen.handleCommands();
	}

	public void searchDisplayUpdate(char value) {
		titleSearchScreen.searchDisplayUpdate(value);
	}

	public void shiftSearchCursor() {
		titleSearchScreen.shiftSearchCursor();
	}

	public State getState() {
		return state;
	}

	public void searchChoiceCompleted(Mp3File mp3) {
		titleSearchScreen.handleCommands();

		if ((state.equals(State.OVERRIDE_MODE)) || (noMp3Running())) {
			try {
				secureSetCurrent(mp3);
				runCurrentMp3();
			} catch (Throwable t) {
				alert(t);
			}
		} else {
			navigator.setLast(mp3);
			titleSearchScreen.alert("Tune added to playlist", 1000);
		}

		rebootSearch();
		setDisplay(titleSearchScreen);
	}

	private void runCurrentMp3() {
		try {
			runMP3(navigator.getCurrent());
		} catch (Throwable e) {
			alert(e);
		}
	}

	private void secureSetCurrent(Mp3File mp3) {
		if (navigator == null) {
			navigator = new Navigator(mp3);
		} else {
			navigator.setCurrent(mp3);
		}
	}

	private boolean noMp3Running() {
		return ((!state.equals(State.PAUSE_MP3)) && (!state
				.equals(State.PLAY_MP3)));
	}

	public void searchEngaged() {
		state.add(State.SEARCH_ENGAGED);
		if (state.equals(State.WICKED_KEY_PRESSED)) {
			currentSearchFunction = wickedSearchFunction;
		}
		titleSearchScreen.handleCommands();
		titleSearchScreen.showSearchInput();
	}

	public void cancelSearch() {
		titleSearchScreen.createSearchInput(false);
		state.remove(State.SEARCH_CHOICE);
		state.remove(State.SEARCH_ENGAGED);
		titleSearchScreen.handleCommands();
		alert("Search canceled.");
		titleSearchScreen.rebootSearch();
	}

	public static State state() {
		return instance.getState();
	}

	public static void removeState(State aState) {
		instance.getState().remove(aState);
	}

	public static void addState(State aState) {
		instance.getState().add(aState);
	}

	public static void repaint() {
		instance.titleSearchScreen.repaint();
		instance.titleSearchScreen.handleCommands();
	}

	public static void resetNightModeIndex() {
		instance.nightModeIndex = 5;
	}

	public static void switchToNewList() {
		Display.getDisplay(instance).setCurrent(instance.newListScreen);
	}

	public void cancelNewList() {
		Display.getDisplay(instance).setCurrent(instance.titleSearchScreen);
	}

	public void pausePlayer() {

		if (player != null) {
			int numStep = 10;

			for (int i = 0; i < numStep; i++) {
				new Timer().schedule(new FadeTask((numStep - 1 - i) * 100
						/ (numStep - 1)), i * 2000 / (numStep - 1));
			}

			new Timer().schedule(new TimerTask() {
				public void run() {
					try {
						player.stop();
						state.add(State.PAUSE_MP3);
						titleSearchScreen.handleCommands();
					} catch (MediaException e) {
						alert(e);
					}
				}
			}, 2000);

			state.remove(State.PLAY_MP3);
			titleSearchScreen.handleCommands();
			titleSearchScreen.repaint();
		}

	}

	public void playPlayer() {
		try {
			if (player != null) {
				player.start();
				int numStep = 10;

				for (int i = 0; i < numStep; i++) {
					new Timer().schedule(new FadeTask(i * 100 / (numStep - 1)),
							i * 2000 / (numStep - 1));
				}
			}
			state.remove(State.PAUSE_MP3);
			titleSearchScreen.handleCommands();
			titleSearchScreen.repaint();

			new Timer().schedule(new TimerTask() {
				public void run() {
					state.add(State.PLAY_MP3);
					titleSearchScreen.handleCommands();
				}
			}, 2000);

		} catch (MediaException t) {
			alert(t);
		}
	}

	private void setMaxVolume() {
		VolumeControl volumeControl = PlayerUtils.getVolumeControl(player);
		if (volumeControl != null) {
			volumeControl.setLevel(100);
		}
	}

	private class FadeTask extends TimerTask {

		int volumeLevel;

		public FadeTask(int aVolumeLevel) {
			volumeLevel = aVolumeLevel;
		}

		public void run() {
			VolumeControl volumeControl = PlayerUtils.getVolumeControl(player);
			if (volumeControl != null) {
				volumeControl.setLevel(volumeLevel);
			}
		}

	}

	public void backToSearchChoice() {
		setDisplay(titleSearchScreen);
	}

	public void popUp(Mp3File mp3) {
		PopUpNameScreen popupScreen = new PopUpNameScreen(mp3);
		setDisplay(popupScreen);
		popupScreen.setup();
	}

	public void readyToGo() {
		setDisplay(titleSearchScreen);
	}

	public void deleteCurrentMp3() {
		try {
			player.stop();
			player.deallocate();
			FileConnectionUtils.secureDelete(navigator.getCurrent());
			navigator.removeCurrent();
			titleSearchScreen.setMetadata(new MP3Metadata("", ""));
			titleSearchScreen.repaint();

			Mp3File mp3 = NowManager.selectNewMp3FromBoot();
			navigator.setCurrent(mp3);
			securePlayCurrentMp3();

			setDisplay(new DeleteReportScreen());
		} catch (Throwable e) {
			e.printStackTrace();
		}
	}

	public void showStats() {
		setDisplay(new StatsScreen());
	}

	private void setDisplay(Canvas canvas) {
		Display.getDisplay(this).setCurrent(canvas);
		canvas.repaint();
	}

	public void lockScreen() {
		setDisplay(new LockScreen());
	}
}
